package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/client"
	"github.com/docker/go-connections/nat"
)

func main() {
	cli, err := client.NewClientWithOpts(client.FromEnv) // 连接本地的docker
	if err != nil {
		panic(err)
	}

	imageName := "mysql:8"

	args := filters.NewArgs(filters.Arg("reference", imageName))
	images, err := cli.ImageList(context.Background(), types.ImageListOptions{Filters: args})
	if err != nil {
		panic(err)
	}
	if len(images) == 0 {
		// ImagePull 请求 docker 主机从远程注册表中提取图像。
		// 如果操作未经授权，它会执行特权功能并再试一次。由调用者来处理 io.ReadCloser 并正确关闭它。
		out, err := cli.ImagePull(context.Background(), imageName, types.ImagePullOptions{})
		if err != nil {
			panic(err)
		}
		defer out.Close()
		// os.Stdout是指向标准输出描述符的打开文件。
		io.Copy(os.Stdout, out)
	}

	ctx := context.Background()

	resp, err := cli.ContainerCreate(ctx,
		// 容器的配置数据。只保存容器的可移植配置信息。
		// “可移植”意味着“独立于我们运行的主机”(容器内部的配置)。
		&container.Config{
			Image: imageName, // 对应docker镜像的名字
			ExposedPorts: nat.PortSet{ // 要暴露端口列表
				"3306/tcp": {},
			},
			Env: []string{
				"MYSQL_ALLOW_EMPTY_PASSWORD=true",
				"TZ=Asia/Shanghai",
				fmt.Sprintf("MYSQL_DATABASE=%s", "mxshop_user_srv"),
			},
			Cmd: []string{
				"--character-set-server=utf8mb4",
				"--collation-server=utf8mb4_general_ci",
				"--default-authentication-plugin=mysql_native_password",
				"--explicit_defaults_for_timestamp=true",
				"--lower_case_table_names=1",
			},
		},

		// 主机的配置。保存不可移植的配置信息。
		&container.HostConfig{
			PortBindings: nat.PortMap{
				"3306/tcp": []nat.PortBinding{
					{
						HostIP:   "0.0.0.0",
						HostPort: "0",
					},
				},
			},
		}, nil, nil, "")
	if err != nil {
		panic(err)
	}

	// ContainerStart 根据containerID,向 docker 守护进程发送请求以启动容器。
	err = cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
	if err != nil {
		panic(err)
	}
	fmt.Println("container started")

	// ContainerInspect 返回容器信息。
	insRes, err := cli.ContainerInspect(ctx, resp.ID)
	if err != nil {
		panic(err)
	}

	// Ports是PortBinding的集合
	fmt.Printf("listening at %+v\n", insRes.NetworkSettings.Ports["3306/tcp"][0])
	fmt.Printf("volume name %+v\n", insRes.Mounts[0].Name)

	time.Sleep(1 * time.Minute)

	// ContainerRemove 根据containerID,杀死并从 docker 主机中删除一个容器。
	err = cli.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{
		Force: true,
	})
	if err != nil {
		panic(err)
	}
}
